<?php

namespace app\service;


use app\exception\ModelEmptyException;
use app\exception\ModelException;
use app\exception\ModelNotUniqueException;
use app\model\BigField;
use app\model\ThemeFile;
use app\model\Theme;
use think\facade\Db;
use think\facade\Log;
use think\response\Json;

class ThemeService
{
    public $themePath;
    public $rootPath;
    public $websitePath;
    public $originalPath;
    public $previewPath;

    public $siteId;
    private $Theme;
    private $ThemeFile;

    public function __construct()
    {
        $this->Theme = new Theme();
        $this->ThemeFile = new ThemeFile();
        $this->rootPath = app()->getRootPath();
        $this->rootPath = str_replace('\\', '/', $this->rootPath);
        $this->themePath = config('view.view_dir_name');
        $this->websitePath = $this->rootPath . 'public/themes/website/';
        $this->originalPath = $this->rootPath . 'public/themes/hc_original/';
        $this->previewPath = $this->rootPath . 'public/themes/preview/';
    }

    /**
     * 卸载模版
     * @throws ModelException
     * @throws ModelEmptyException
     */
    public function uninstallTheme($themeId, $siteId, $sellerId): Json
    {
        Db::startTrans();
        try {
            $theme = $this->getThemeModel($themeId, $siteId, $sellerId);
            $this->updateThemeDelete($theme, $siteId , $sellerId);
            CacheService::deleteRelationCacheByObject($this->Theme);
            CacheService::deleteRelationCacheByObject($this->ThemeFile);
            CacheService::deleteRelationCacheByObject(BigField::class);
            Db::commit();
        } catch (\Exception | ModelException $e) {
            Db::rollback();
            return jsonReturn(-6, $e->getMessage());
        }
        return jsonReturn(0, lang('卸载成功'));
    }

    /**
     * @throws ModelException
     */
    public function updateThemeDelete($theme, $siteId, $sellerId,$isDel = true)
    {
        // 文件真实删除
        $this->deleteFile($this->websitePath . $siteId . '/' . $theme['lang'] . '/' . $theme['theme']);

        if($isDel){
            $this->deleteFile($this->websitePath . $siteId . '/' . $theme['lang'] . '/' . $theme['theme']);
        }
        // 文件数据库删除
        $this->ThemeFile->delThemeFile(['theme_id' => $theme['id'], 'seller_id' => $sellerId, 'website_id' => $siteId]);
        $bigField = new BigField();
        $bigField->delBigField(['theme_id' => $theme['id'], 'seller_id' => $sellerId]);
        $theme->delete();
    }

    /**
     * 模版安装
     * @throws ModelException
     */
    public function installTheme($theme, $siteId, $sellerId,$suffix = 'html',$lang='zh'): Json
    {
        // 站点模版根目录
        if(!$this->hasPath($this->websitePath)){
            mkdir($this->websitePath);
        }
        // 站点目录
        $tmpPath = $this->websitePath . $siteId;
        if(!$this->hasPath($tmpPath)){
            mkdir($tmpPath);
        }
        // 站点语言目录
        $tmpLangPath = $tmpPath . '/' . $lang;
        if(!$this->hasPath($tmpLangPath)){
            mkdir($tmpLangPath);
        }
        // 站点语言模版目录
        $tmpThemePath = $tmpPath . '/' . $lang . '/' . $theme;
        if(!$this->hasPath($tmpThemePath)){
            mkdir($tmpThemePath);
        }else{
            if(!$this->uniqueTheme($theme, $siteId)){
                return jsonReturn(-4, lang('相同模版已经安装,请修改模版名称'));
            }
        }
        // 系统模版路径
        $dir = $this->originalPath . $theme;

        return $this->installThemeFile($theme,$dir,$tmpThemePath,$siteId,$sellerId,$suffix,$lang);
    }

    /**
     * 模版更新
     * @throws ModelException
     * @throws ModelEmptyException
     * @throws \ReflectionException
     */
    public function updateTheme($themeId, $siteId, $sellerId, $suffix = 'html'): Json
    {
        $theme = $this->getThemeModel($themeId,$siteId,$sellerId);

        $dir = $this->websitePath . $siteId . '/' . $theme['lang'] . '/' . $theme['theme'];

        if (!$this->hasPath($dir)) {
            return jsonReturn(-5, lang('模板安装文件不存在'));
        }

        Db::startTrans();
        try {
            $BigField = new BigField();
            $version = $BigField->where([
                ['seller_id', '=', $sellerId],
                ['theme_id', '=', $theme['id']],
            ])->max('version');
            $version = $version ?: 1;

            $param['website_id'] = $siteId;
            $param['lang'] = $theme['lang'];
            $this->updatePreviewFile($param, $version, $theme, true);

            CacheService::deleteRelationCacheByObject($this->Theme);
            CacheService::deleteRelationCacheByObject($this->ThemeFile);
            CacheService::deleteRelationCacheByObject(BigField::class);
        } catch (\Exception $e) {
            Db::rollback();
            Log::error(lang('模版更新失败！') . $e->getMessage() . $e->getTraceAsString());
            return jsonReturn(-5, lang('模版更新失败！'));
        }
        Db::commit();
        return jsonReturn(0, lang('模版更新成功'));
    }

    /**
     * 模版更新
     * @throws ModelException
     * @throws ModelEmptyException
     * @throws \ReflectionException
     */
    public function publishThemeFile($themeId, $siteId, $sellerId, $suffix = 'html', $version = 1): Json
    {
        $theme = $this->getThemeModel($themeId,$siteId,$sellerId);

        $dir = $this->websitePath . $siteId . '/' . $theme['lang'] . '/' . $theme['theme'];

        if (!$this->hasPath($dir)) {
            return jsonReturn(-5, lang('模板安装文件不存在'));
        }

        Db::startTrans();
        try {
            $param['website_id'] = $siteId;
            $param['lang'] = $theme['lang'];
            $this->updatePreviewFile($param, $version, $theme, true);

            CacheService::deleteRelationCacheByObject($this->Theme);
            CacheService::deleteRelationCacheByObject($this->ThemeFile);
            CacheService::deleteRelationCacheByObject(BigField::class);

        } catch (\Exception $e) {
            Db::rollback();
            Log::error(lang('模版更新失败！') . $e->getMessage() . $e->getTraceAsString());
            return jsonReturn(-5, lang('模版更新失败！'));
        }
        Db::commit();
        return jsonReturn(0, lang('模版更新成功'));
    }

    /**
     * 模版文件更新时安装
     */
    public function updateFile($theme,$siteId)
    {
        $tmpPath = $this->websitePath . $siteId . '/' . $theme['lang'] . '/' . $theme['theme'] ;

        $tmpAdminPath = $this->originalPath . $theme['theme'];

        $res = $this->copyFile($tmpAdminPath,$tmpPath);
        if(!$res){
            throw new \Exception(lang('模板安装失败'));
        }
    }

    public static function updatePreviewFile($param, $version, $theme, $isPublish = false)
    {
        // 先将当前已安装的模板移动至preview目录下
        $themeService = new ThemeService();
        // 当前模版路径
        $dir = $themeService->websitePath . $param['website_id'] . '/' . $param['lang'] . '/' . $theme['theme'];
        $preViewPath = $themeService->rootPath . 'public/themes/preview/' . $theme['theme'];// 预览模板路径
        $themeService->deleteFile($preViewPath); //先删除
        $res = $themeService->copyFile($dir,$preViewPath); // 再复制过来

        if(!$res){
            return jsonReturn(-2, '预览失败，请检查文件夹权限！');
        }

        $bigFieldModel = new BigField();
        $fileList = $bigFieldModel->alias('a')
            ->join('theme_file b', 'a.theme_file_id = b.id')
            ->where([
                ['a.version', '=', intval($version)],
                ['a.theme_id', '=', $theme['id']]
            ])->field('a.*, b.file')->select()->toArray();

        // 将当前版本的文件内容替换为需要展示的版本内容
        foreach ($fileList as $fileInfo) {
            $path = $preViewPath . '/' . $fileInfo['file'];
            file_put_contents($path, $fileInfo['content']);
            if ($isPublish) {
                $path = $dir . '/' . $fileInfo['file'];
                file_put_contents($path, $fileInfo['content']);
            }
        }
    }

    /**
     * 安装模版文件公共方法
     * @param $theme
     * @param $dir
     * @param $tmpThemePath
     * @param $siteId
     * @param $sellerId
     * @param $suffix
     * @param $lang
     * @return Json
     */
    private function installThemeFile($theme, $dir, $tmpThemePath, $siteId, $sellerId, $suffix, $lang): Json
    {
        Db::startTrans();
        try {
            // 获取模版配置文件
            if(hcFileExist($dir. '/' . 'manifest.json')){
                $themeData = json_decode(file_get_contents($dir. '/' . 'manifest.json'),true);
            }
            $themeData['theme'] = $theme;
            $themeData['lang'] = $lang;
            $themeData['seller_id'] = $sellerId;
            $themeData['website_id'] = $siteId;
            $themeData['is_active'] = 2;
            $theme = $this->Theme->addTheme($themeData)['data'];
            $res = $this->copyFile($dir,$tmpThemePath);
            $this->installThemeFiles($dir,$theme, $siteId, $sellerId, $suffix,$lang);
            if(!$res){
                throw new \Exception(lang('模板安装失败'));
            }
            // 没有激活模版，激活当前安装的模版
            $activeTheme = $this->Theme->getActiveTheme(['seller_id'=>$sellerId,'website_id'=>$siteId,'lang'=>$lang,'is_active'=>1])['data'];
            if(empty($activeTheme)){
                $theme->is_active = 1;
                $theme->save();
            }
            CacheService::deleteRelationCacheByObject($this->Theme);
            CacheService::deleteRelationCacheByObject($this->ThemeFile);
            CacheService::deleteRelationCacheByObject(BigField::class);

            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            $this->deleteFile($tmpThemePath);
            Log::error($e->getMessage() . $e->getTraceAsString());
            return jsonReturn(-8, $e->getMessage());
        }
        return jsonReturn(0, lang('模版安装成功'));

    }

    private function getTemplateFileFromThemePath($dir,$suffix): array
    {
        $tplFiles = $this->getDirTmp($dir, $suffix);
        $subDirs = scanSubDir($dir);
        foreach ($subDirs as $subDir) {
            $subDirTplFileArr = $this->getDirTmp($subDir, $suffix);
            $tplFiles = array_merge($tplFiles, $subDirTplFileArr);
        }
        return $tplFiles;
    }

    /**
     * @throws ModelException
     */
    private function installThemeFiles($dir, $theme, $siteId, $sellerId, $suffix, $lang)
    {
        $tplFiles = $this->getTemplateFileFromThemePath($dir,$suffix);
        $themeDir = str_replace('\\', '/', $dir);
        // 模版公共文件路径
        foreach ($tplFiles as $tplFile) {
            $tplFile = str_replace('\\', '/', $tplFile);
            $tmpFilePath =  $tplFile;
            $file = str_replace(strtolower($themeDir) . '/' , '', strtolower($tplFile));
            $realPath =  '/' . $siteId . '/' . $lang . '/' . str_replace($this->originalPath,'',$tmpFilePath);
            $BigField = new BigField();
            $content = htmlspecialchars_decode(file_get_contents($tmpFilePath));
            $tmpFile = $this->ThemeFile->addThemeFile([
                'theme_id' => $theme['id'],
                'lang' => $lang,
                'website_id' => $siteId,
                'seller_id' => $sellerId,
                'theme_name' => $theme['theme'],
                'file' => $file,
                'file_hash' => hash_file('md5',$tplFile),
                'real_path' => $realPath,
                'name' => basename($file),
            ])['data'];
            // 保存模板文件历史记录
            $BigField->addBigField([
                'seller_id' => $sellerId,
                'theme_id' => $theme['id'],
                'theme_file_id' => $tmpFile->id,
                'content' => $content,
                'admin_id' => session('adminId'),
            ]);
        }
    }

    /**
     * 更新模版存储数据
     * @param $dir
     * @param int $siteId
     * @param string $suffix
     * @throws ModelException
     */
    private function updateThemeFiles($dir, $theme,int $siteId, $sellerId, string $suffix = 'html',$lang = 'zh')
    {
        $tplFiles = $this-> getTemplateFileFromThemePath($dir,$suffix);
        $themeDir = str_replace('\\', '/', $dir);
        // 对比数据库的保存的模版文件和更新的模版文件
        $themeFile = new ThemeFile();
        $dbTmpFile =  $themeFile->getAllCustomDataModel(['website_id'=>$siteId,'seller_id'=>$sellerId,'lang'=>$lang,'theme_id'=>$theme['id']])['data'];
        $dbTmpFile = $dbTmpFile ? $dbTmpFile->toArray() : [];

        // 不要动斜杠 ！！！！！！！！！！！！！ 要动就要所有斜杠的地方可能都要动，动完记得测试！！！！！！
        $themePath = $this->websitePath . $siteId . '/' . $theme['lang'] . '/' . $theme['theme'] . '/';

        if(!empty($dbTmpFile)){
            $dbTmpFile = array_column($dbTmpFile,'file');
            $tmpTplFiles = [];
            foreach ($tplFiles as $val){
                if(!empty($val)){
                    $val = str_replace('\\', '/', $val);
                    $tmpTplFiles[] = str_replace( $themePath,'',$val);
                }
            }
            $delTpl = array_diff($dbTmpFile,$tmpTplFiles);
            if(!empty($delTpl)){
                $delWhere = [
                    ['website_id','=',$siteId],
                    ['seller_id','=',$sellerId],
                    ['lang','=',$lang],
                    ['theme_id','=',$theme['id']],
                    ['file','in',$delTpl],
                ];
                $themeFile -> delThemeFile($delWhere);
            }
        }

        $BigField = new BigField();
        $version = $BigField->where([
            ['seller_id', '=', $sellerId],
            ['theme_id', '=', $theme['id']],
        ])->max('version');
        $version = $version ?: 0;
        $version++;

        foreach ($tplFiles as $tplFile) {
            $tplFile = str_replace('\\', '/', $tplFile);
            $tmpFilePath =  $tplFile;
            $file = str_replace(strtolower($themeDir) . '/' , '', strtolower($tplFile));
            $realPath = '/' . str_replace($this->websitePath,'',$tplFile);
            $content = htmlspecialchars_decode(file_get_contents($tmpFilePath));

            // 获取最新版本
            self::saveThemeFileHistory($siteId, $lang, $theme, $sellerId, $file, '', $content, $tplFile, $realPath, $version);
        }

        return $version;
    }

    public static function saveThemeFileHistory($siteId, $lang, $theme, $sellerId, $file, $filename, $content, $tplFile = '', $realPath = '', $version = 1)
    {
        $themeFileModel = new ThemeFile();
        if (empty($realPath)) {
            $realPath = "/{$siteId}/{$lang}/{$theme['theme']}/{$filename}.html";
        }
        $findFile = $themeFileModel->existThemeFile(['theme_id' => $theme['id'], 'seller_id' => $sellerId, 'website_id' => $siteId,'lang'=>$lang, 'file' => $file]);
        $findFile = $findFile['data'];
        $BigField = new BigField();

        if (!empty($tplFile)) {
            $md5TplFile = hash_file('md5',$tplFile);
        } else {
            $md5TplFile = md5($content);
        }

        if (empty($findFile['id'])) {
            // 直接新增的
            $tmpFile = $themeFileModel->addThemeFile([
                'theme_id' => $theme['id'],
                'lang' => $lang,
                'website_id' => $siteId,
                'seller_id' => $sellerId,
                'theme_name' => $theme['theme'],
                'file' => $file,
                'file_hash' => $md5TplFile,
                'real_path' => $realPath,
                'name' => basename($file),
            ])['data'];
            // 保存模板文件历史记录
            $BigField->addBigField([
                'seller_id' => $sellerId,
                'theme_id' => $theme['id'],
                'theme_file_id' => $tmpFile->id,
                'content' => $content,
                'version' => $version,
                'admin_id' => session('adminId'),
            ]);
        } else {
            // 更新文件
            $themeFileModel->updateThemeFile(['theme_id' => $theme['id'], 'seller_id' => $sellerId, 'website_id' => $siteId, 'id' => $findFile['id']], [
                'theme_name' => $theme['theme'],
                'file' => $file,
                'file_hash' => $md5TplFile,
                'real_path' => $realPath,
                'name' => $file,
            ]);
            // 保存模板文件历史记录

            $BigField->addBigField([
                'seller_id' => $sellerId,
                'theme_id' => $theme['id'],
                'theme_file_id' => $findFile['id'],
                'content' => $content,
                'version' => $version,
                'admin_id' => session('adminId'),
            ]);
        }

    }

    /**
     * 获取需要操作的模板模型
     * @throws ModelException
     * @throws ModelEmptyException
     */
    public function getThemeModel($themeId, $siteId, $sellerId)
    {
        return $this->Theme->getTheme(['id'=>$themeId,'seller_id' => $sellerId,'website_id' => $siteId])['data'];
    }

    public function hasPath($path): bool
    {
        if (is_dir($path)) {
            return true;
        }
        return false;
    }

    /**
     * @throws ModelException
     */
    public function uniqueTheme($theme, $websiteId): bool
    {
        try {
            $this->Theme->saveUnique(['theme'=>$theme,'website_id'=>$websiteId]);
        }catch (ModelNotUniqueException $e){
            return false;
        }
        $dir = $this->websitePath . $websiteId;
        $files = hcScanDir("$dir/*");
        if(empty($files)){
            return true;
        }
        foreach($files as $file){
            $manifest = $dir. '/' . $file . '/' . 'manifest.json';
            if(hcFileExist($manifest)){
                $config = file_get_contents($manifest);
                $config = json_decode($config,true);
                if($theme === $config['name']){
                    return false;
                }
            }
        }
        return true;
    }

    // 获取文件名不带后缀
    public function getFilenameWithoutExt($suffix,$tmpFile)
    {
        return preg_replace("/\.$suffix$/", '', $tmpFile);
    }


    /**
     * 获取目录下模版文件
     * @param $dir
     * @param string $suffix
     */
    public function getDirTmp($dir, string $suffix = 'html'): array
    {
        $tmpFiles = [];
        $subDirTplFiles = hcScanDir("$dir/*.$suffix");

        foreach ($subDirTplFiles as $tmpFile) {
            $tmpFile = $dir . '/' . $tmpFile;
            $tmpFiles[] = $tmpFile;
        }
        return $tmpFiles;
    }

    /**
     * 文件夹列表
     * @throws ModelEmptyException
     * @throws ModelException
     */
    public function filesPath($siteId,$lang='zh'): Json
    {
        $pathName = $this->websitePath . $siteId . '/' .$lang;
        $res = $this->recurDir($pathName,$siteId,$pathName,$lang);
        return jsonReturn(0, lang('成功'), $res);
    }

    /**
     * 文件列表
     * @param $path
     * @param int $siteId
     * @return Json
     */
    public function files($path,int $siteId,$lang='zh'): Json
    {
        $pathName = rtrim($this->websitePath,'/') . $path;
        $this->siteId = $siteId;

        $tmpPath = str_replace('\\', '/', $this->websitePath .$siteId);
        $files = $this->getFiles($pathName,$tmpPath);
        return jsonReturn(0, lang('成功'), $files);
    }

    public function getFiles($pathName,$tmpPath): array
    {
        $files = [];
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return $files;
        }
        $res = scandir($pathName);
        foreach($res as $file){
            if (in_array($file, array('.', '..'))) {
                continue;
            }
            $fullPath = $pathName .'/' .$file;
            $fullPath = str_replace('\\', '/', $fullPath);

            if(is_dir($fullPath)){
                $tmp = [
                    'type'  => 'path',
                ];
            }else{
                $tmp = [
                    'type'  => fileFormat($file),
                ];
            }
            $tmp['path'] = str_replace($tmpPath,'',$fullPath);
            $tmp['title'] = $file;
            $files[] = $tmp;
        }
        return $files;
    }

    /**
     * 获取树形目录结构
     *
     * @param $pathName
     * @return array
     */
    public function recurDir($pathName,$siteId,$tmpPath,$lang): array
    {
        //将结果保存在result变量中
        $result = array();
        //判断传入的变量是否是目录
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return $result;
        }
        //取出目录中的文件和子目录名,使用scandir函数
        $allFiles = scandir($pathName);
        //遍历他们
        $i = 0;
        foreach ($allFiles as $fileName) {
            //判断是否是.和..因为这两个东西神马也不是。。。
            if (in_array($fileName, array('.', '..'))) {
                continue;
            }
            //路径加文件名
            $fullName = $pathName . '/' . $fileName;
            //如果是目录的话就继续遍历这个目录
            if (is_dir($fullName)) {
                //将这个目录中的文件信息存入到数组中
                $path = '/' .$siteId . '/' . $lang . str_replace($tmpPath,'',$fullName);
                $result[$i] = [
                    'title' => $fileName,
                    'path'  => $path,
                    'child' => $this->recurDir($fullName,$siteId,$tmpPath,$lang),
                    'key' => md5($path)
                ];
                $i++;
            }

        }
        return $result;
    }

    /**
     * 文件复制
     * @param $source
     * @param $destination
     * @param bool $child
     * @return bool
     */
    public function copyFile($source, $destination, bool $child = true): bool
    {
        if (!is_dir($source)) {
            return false;
        }
        if(!$this->hasPath($destination)){
            mkdir($destination, 0755);
        }

        $handle = dir($source);

        while($entry = $handle->read()){
            if($entry != '.' && $entry != '..') {
                if (is_dir($source. '/'.$entry)) {
                    if ($child) {
                        $this->copyFile($source . '/' . $entry, $destination . '/' . $entry, $child);
                    }
                } else {
                    copy($source . '/' . $entry, $destination . '/' . $entry);
                }
            }
        }
        return true;
    }

    /**
     * 文件删除
     * @param $path
     * @return bool
     */
    public function deleteFile($path): bool
    {
        if(is_dir($path) && $this->hasPath($path)){
            //扫描一个文件夹内的所有文件夹和文件并返回数组
            $p = scandir($path);
            //如果 $p 中有两个以上的元素则说明当前 $path 不为空
            if(count($p)>2){
                foreach($p as $val){
                    //排除目录中的.和..
                    if($val !="." && $val !=".."){
                        //如果是目录则递归子目录，继续操作
                        if(is_dir($path. '/' .$val)){
                            //子目录中操作删除文件夹和文件
                            $this->deleteFile($path. '/' .$val. '/');
                        }else{
                            //如果是文件直接删除
                            unlink($path. '/' .$val);
                        }
                    }
                }
            }
        }
        if($this->hasPath($path)){
            //删除目录
            return rmdir($path);
        }else{
            return true;
        }
    }

    /**
     * 格式化后台模版文件给前台
     * @param $html
     * @return array|string|string[]|null
     */
    public function pureBackendEditCode(&$html)
    {
        $pattern = "/([.\\n])?data-block-id=([\w\W]*?)data-hc-block-control=\"true\"([.\\n])?/";
        $html = preg_replace($pattern,'',$html);
        $pattern1 = '/\{include file=\"public\/backend_edit\" \}/';
        $html =  preg_replace($pattern1,'',$html);
        return $html;
    }

    /**
     * 设置前排模版内容
     * @param $content
     * @param $originalPath
     * @param $realpath
     * @param $tmpFilePath
     */
    public function setRealContent($content,$originalPath,$realpath,$tmpFilePath)
    {
        $realPath = str_replace($originalPath,$realpath,$tmpFilePath);

        $realContent = $this->pureBackendEditCode($content);

        file_put_contents($realPath,$realContent);
    }

    public function isPath($pathName): bool
    {
        //判断传入的变量是否是目录
        if (!is_dir($pathName) || !is_readable($pathName)) {
            return false;
        }
        return true;
    }

    public function delOriginalTheme($theme): array
    {
        $themePath = $this->originalPath . $theme;
        if($this->hasPath($themePath)){
            $this->deleteFile($themePath);
        }
        return dataReturn(0, lang('删除成功'));
    }
}
